package org.awa.demo.opencv.service.impl;

import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.awa.demo.opencv.bean.MaterialInfo;
import org.awa.demo.opencv.bean.NoiseSource;
import org.awa.demo.opencv.bean.newgltf.*;
import org.awa.demo.opencv.gis.bean.HorizontalElement;
import org.awa.demo.opencv.gltf.NewGltfEncoder;
import org.awa.demo.opencv.gltf.SoundUtil;
import org.awa.demo.opencv.gltf.newhandler.INewRectangleHandler;
import org.awa.demo.opencv.gltf.newhandler.NewRectangleAnalysis;
import org.awa.demo.opencv.gltf.newhandler.NewRectangleAssigner;
import org.awa.demo.opencv.service.INewGltfService;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.grid.Grids;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.math.Vector3D;
import org.opengis.feature.simple.SimpleFeature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class NewGltfServiceImpl implements INewGltfService {

    @Autowired
    private NewGltfEncoder gltfEncoder;

    @Autowired
    private NewRectangleAssigner assigner;

    @Autowired
    private NewRectangleAnalysis analysis;

    @Override
    public NewGltfRequestDto getGltf() throws Exception {
        NewGltfRequestDto requestDto = new NewGltfRequestDto();
        requestDto.setGltfPath("http://115.236.86.166:9000/doc/bangongshiv2.glb");
        requestDto.setGltfUVPath("http://115.236.86.166:9000/doc/bangongshiv2-uv.glb");

        String filePath = "E:\\blenderworkspace\\bangongshiv2\\bangongshiv2-uv.glb";
        //解析glb文件，获取面列表以及相关信息
        List<PlaneBean> planeList = getPrimitiveList(filePath);
        if (planeList.size() == 0) {
            requestDto.setFaceList(new ArrayList<>());
            return requestDto;
        }

        planeList.stream().forEach(planeBean -> {
            log.info("print plane: " + planeBean.toString());
        });

        List<NewFaceItemDto> faceList = new ArrayList<>();

        for (int i = 0; i < planeList.size(); i++) {
            PlaneBean planeBean = planeList.get(i);
            //获取面轮廓数据以及网格位置数据
            INewRectangleHandler rectangleHandler = assigner.getRectangleHandler(planeBean.getPointListBlender());
            NewFaceItemDto face = analysis.getFace(planeBean, rectangleHandler);
            faceList.add(face);
        }
        requestDto.setFaceList(faceList);

        //随机生成材料列表
        List<MaterialInfo> materialInfos = randomMaterial();

        //计算R值
        double R = SoundUtil.calInDoorR(materialInfos);
        log.info("R值计算完成：" + R);

        //随机生成噪声源
        List<NoiseSource> noiseSourceList = getNoiseSource(faceList,R);
        requestDto.setNoiseSourceList(noiseSourceList);

        //增加测点数据
        calNoiseLwList(faceList,noiseSourceList);

        for (int i = 0; i < faceList.size(); i++) {
            NewFaceItemDto face = faceList.get(i);
            //根据网格获取leqt数据
            calLeqtInFace(face, noiseSourceList);
        }

        return requestDto;
    }

    private void calLeqtInFace(NewFaceItemDto faceItemDto, List<NoiseSource> noiseSourceList) {
        List<NewGridItemDto> gridList = faceItemDto.getGridList();
        for (int i = 0; i < gridList.size(); i++) {
            NewGridItemDto gridItemDto = gridList.get(i);
            NewPointItemDto gridPoint = SoundUtil.getPointBean(gridItemDto.getPointCrs());

            List<NewGridMeasureDistanceItemDto> distanceListInMeasure = noiseSourceList.stream().map(noiseSource -> {
                NewGridMeasureDistanceItemDto itemDto = new NewGridMeasureDistanceItemDto(noiseSource, gridPoint);
                itemDto.setDistance(SoundUtil.getDistance(noiseSource.getLocation(), gridPoint.toVector3D()));
                return itemDto;
            }).sorted(Comparator.comparing(NewGridMeasureDistanceItemDto::getDistance)).collect(Collectors.toList());

            List<Double> leqtList = filterGridMeasure(distanceListInMeasure).stream().map(gridMeasure -> {
                return SoundUtil.calAttenuationLeptPointInDoor(gridMeasure.getDistance(), gridMeasure.getNoiseSource());
            }).collect(Collectors.toList());

            Double leqt = SoundUtil.soundSuperposition(leqtList);
            gridItemDto.setLeqt(BigDecimal.valueOf(leqt).setScale(1, RoundingMode.HALF_DOWN).doubleValue());
        }
    }

    private List<NewGridMeasureDistanceItemDto> filterGridMeasure(List<NewGridMeasureDistanceItemDto> gridMeasureList) {
        if (gridMeasureList == null || gridMeasureList.size() == 0) {
            return new ArrayList<>();
        }

        NewGridMeasureDistanceItemDto itemDto = gridMeasureList.get(0);
        if (itemDto.getDistance() < 4) {
            List<NewGridMeasureDistanceItemDto> result = new ArrayList<>();
            result.add(itemDto);
            return result;
        }

        return gridMeasureList;
    }

    private List<PlaneBean> getPrimitiveList(String filePath) throws Exception {
        File file = new File(filePath);
        if (!file.exists()) {
            return new ArrayList<>();
        }

        return gltfEncoder.encode(file);
    }

    public void calNoiseLwList(List<NewFaceItemDto> faceList,List<NoiseSource> noiseSourceList) throws Exception {
        //根据轮廓值获取3维范围
        if(noiseSourceList == null || noiseSourceList.size() == 0) {
            return;
        }
        BigDecimal[] range = getRange(faceList);

        for (NoiseSource noiseSource : noiseSourceList) {
            Vector3D point = new Vector3D(
                    RandomUtil.randomBigDecimal(range[0],range[1]).setScale(2,RoundingMode.HALF_DOWN).doubleValue(),
                    RandomUtil.randomBigDecimal(range[2],range[3]).setScale(2,RoundingMode.HALF_DOWN).doubleValue(),
                    RandomUtil.randomBigDecimal(range[4],range[5]).setScale(2,RoundingMode.HALF_DOWN).doubleValue()
            );
            double lp = randomLeqt();
            noiseSource.calLw(lp,point);
            log.info("生成随机噪声源,lp:{},point:{},bean:{}",lp,point,noiseSource.toString());
        }
    }

    //随机生成噪声源
    public List<NoiseSource> getNoiseSource(List<NewFaceItemDto> faceList,double R){
        if(faceList == null || faceList.size() == 0) {
            return new ArrayList<>();
        }
        BigDecimal[] range = getRange(faceList);

        List<NoiseSource> resultList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Vector3D point = new Vector3D(
                RandomUtil.randomBigDecimal(range[0],range[1]).setScale(2,RoundingMode.HALF_DOWN).doubleValue(),
                RandomUtil.randomBigDecimal(range[2],range[3]).setScale(2,RoundingMode.HALF_DOWN).doubleValue(),
                RandomUtil.randomBigDecimal(range[4],range[5]).setScale(2,RoundingMode.HALF_DOWN).doubleValue()
            );

            NoiseSource noiseSource = NoiseSource.builder().Q(randomQ()).R(R).location(point).build();
            resultList.add(noiseSource);
        }
        return resultList;
    }

    //随机生成材料
    private List<MaterialInfo> randomMaterial(){
        List<MaterialInfo> materialInfoList = new ArrayList<>();

        BigDecimal minBigDecimal = BigDecimal.valueOf(5);
        BigDecimal maxBigDecimal = BigDecimal.valueOf(50);

        BigDecimal minRateBigDecimal = BigDecimal.valueOf(0.4);
        BigDecimal maxRateBigDecimal = BigDecimal.valueOf(0.8);

        for (int i = 0; i < 5; i++) {
            MaterialInfo materialInfo = new MaterialInfo();
            materialInfo.setArea(RandomUtil.randomBigDecimal(minBigDecimal,maxBigDecimal).setScale(2,RoundingMode.HALF_DOWN).floatValue());
            materialInfo.setSoundAbsorption(RandomUtil.randomBigDecimal(minRateBigDecimal,maxRateBigDecimal).setScale(2,RoundingMode.HALF_DOWN).floatValue());
            log.info(">>>生成随机材料: "+materialInfo);
            materialInfoList.add(materialInfo);
        }

        return materialInfoList;
    }

    private Integer randomQ(){
        int type = RandomUtil.randomInt(1, 4);
        switch (type){
            case 1:
                return 1;
            case 2:
                return 2;
            case 3:
                return 4;
            case 4:
                return 8;
            default:
                return 1;
        }
    }

    private Double randomLeqt() {
        return BigDecimal.valueOf(RandomUtil.randomInt(500, 900)).divide(BigDecimal.valueOf(10), 1, RoundingMode.HALF_DOWN).doubleValue();
    }

    private BigDecimal[] getRange(List<NewFaceItemDto> faceList){
        BigDecimal xMin = null;
        BigDecimal xMax = null;
        BigDecimal yMin = null;
        BigDecimal yMax = null;
        BigDecimal zMin = null;
        BigDecimal zMax = null;

        for (int i = 0; i < faceList.size(); i++) {
            NewFaceItemDto faceItemDto = faceList.get(i);
            for (int j = 0; j < faceItemDto.getScopeList().size(); j++){
                NewPointItemDto pointItemDto = faceItemDto.getScopeList().get(j);
                BigDecimal x = BigDecimal.valueOf(pointItemDto.getX());
                BigDecimal y = BigDecimal.valueOf(pointItemDto.getY());
                BigDecimal z = BigDecimal.valueOf(pointItemDto.getZ());

                if(xMin == null || xMin.compareTo(x) > 0){
                    xMin = x;
                }
                if(xMax == null || xMax.compareTo(x) < 0){
                    xMax = x;
                }
                if(yMin == null || yMin.compareTo(y) > 0){
                    yMin = y;
                }
                if(yMax == null || yMax.compareTo(y) < 0){
                    yMax = y;
                }
                if(zMin == null || zMin.compareTo(z) > 0){
                    zMin = z;
                }
                if(zMax == null || zMax.compareTo(z) < 0){
                    zMax = z;
                }
            }
        }
        return new BigDecimal[]{
                xMin, xMax, yMin, yMax, zMin, zMax
        };
    }

    public static void main(String[] args) {
        try{
            Coordinate[] coordinates = new Coordinate[]{
                    new Coordinate(-7.2525,-6.6536),
                    new Coordinate(4.1897,-6.6536),
                    new Coordinate(4.1897,2.4326),
                    new Coordinate(8.0879,2.4326),
                    new Coordinate(8.0879,7.2173),
                    new Coordinate(-7.2525,7.2110),
                    new Coordinate(-7.2525,-6.6536)
            };

            GeometryFactory geomFactory = new GeometryFactory();
            // 创建Polygon（闭合轮廓）
            LinearRing ring = geomFactory.createLinearRing(coordinates);
            Polygon polygon = geomFactory.createPolygon(ring);

            //水平范围 确定范围（获取边界）
            Envelope envelopeInternal = polygon.getEnvelopeInternal();

            //栅格化
            SimpleFeatureSource grid = Grids.createSquareGrid(new ReferencedEnvelope(
                    envelopeInternal.getMinX(),envelopeInternal.getMaxX(),
                    envelopeInternal.getMinY(),envelopeInternal.getMaxY(),
                    DefaultGeographicCRS.WGS84),0.5);//WGS84 说是没用，看看效果再说
            SimpleFeatureIterator iterator = grid.getFeatures().features();

            while (iterator.hasNext()){
                SimpleFeature feature = iterator.next();
                Geometry geometry = (Geometry) feature.getDefaultGeometry();
                if(geometry.intersects(polygon)){
                    //判断相交，获取质心坐标，以及交线长度，存入水平栅格数组。水平栅格数组与高度配合形成空间栅格数组
                    log.info("生成栅格:{}",geometry.toText());
                }
            }

            //
        }catch (Exception e){
            e.printStackTrace();
        }

    }

}
